/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Authors:
 *	lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#ifndef _LOMBO_VO_MANAGE_H_
#define _LOMBO_VO_MANAGE_H_

struct lombo_vo_host;
struct lombo_vo_device;

/**
 * struct lombo_vo_msg - read/write lombo disp-if buffer
 * @type: payload data type
 * @flags: flags controlling this message transmission
 * @tx_len: length of @tx_buf
 * @tx_buf: data to be written
 * @rx_len: length of @rx_buf
 * @rx_buf: data to be read, or NULL
 */
struct lombo_vo_msg {
	u8 type;
	u16 flags;

	size_t tx_len;
	void *tx_buf;

	size_t rx_len;
	void *rx_buf;
};

struct lombo_vo_host_ops {
	int (*attach)(struct lombo_vo_host *host,
			    struct lombo_vo_device *vo);
	int (*detach)(struct lombo_vo_host *host,
			    struct lombo_vo_device *vo);
	ssize_t (*transfer)(struct lombo_vo_host *host,
			    const struct lombo_vo_msg *msg);
};

typedef enum tag_lombo_vo_msg_type {
	LOMBO_CPU_IF_MSG_WR_IDX		= 0x0,
	LOMBO_CPU_IF_MSG_WR_PARA	= 0x1,
	LOMBO_CPU_IF_MSG_WR_REG		= 0x2,
	LOMBO_CPU_IF_MSG_RD_PARA	= 0x3,
	LOMBO_CPU_IF_MSG_RD_REG		= 0x4,
	LOMBO_CPU_IF_MSG_WR_GRAM	= 0x5,
	LOMBO_CPU_IF_MSG_RD_GRAM	= 0x6,
} lombo_vo_msg_type_t;

struct lombo_vo_host {
	struct device *dev;
	uint32_t host_id;
	const struct lombo_vo_host_ops *ops;
	struct list_head list;
};

#define VO_DEV_NAME_SIZE 20

struct lombo_vo_device_info {
	char type[VO_DEV_NAME_SIZE];
	u32 channel; /* vo output channel: 0:lvds 1:rgb 2:cpu 3:tve */
	struct device_node *node;
};

enum lombo_vo_pixel_format {
	LOMBO_VO_FMT_RGB888,
	LOMBO_VO_FMT_RGB666,
	LOMBO_VO_FMT_RGB565,
	LOMBO_VO_FMT_RGB555,
	LOMBO_VO_FMT_YUV444,
	LOMBO_VO_FMT_YUV422,
};

typedef enum lombo_vo_dev_if {
	LOMBO_VO_DEV_PRGB			= 0,
	LOMBO_VO_DEV_SRGB_RGB			= 1,
	LOMBO_VO_DEV_SRGB_DUMMY_RGB		= 2,
	LOMBO_VO_DEV_SRGB_RGB_DUMMY		= 3,
	LOMBO_VO_DEV_CPU_18BIT_RGB666		= 4,
	LOMBO_VO_DEV_CPU_16BIT_RGB888		= 5,
	LOMBO_VO_DEV_CPU_16BIT_RGB666		= 6,
	LOMBO_VO_DEV_CPU_16BIT_RGB565		= 7,
	LOMBO_VO_DEV_CPU_9BIT_RGB666		= 8,
	LOMBO_VO_DEV_CPU_8BIT_RGB666		= 9,
	LOMBO_VO_DEV_CPU_8BIT_RGB565		= 10,
	LOMBO_VO_DEV_CPU_8BIT_RGB888		= 11,
	LOMBO_VO_DEV_LVDS			= 12,
	LOMBO_VO_DEV_MIPI_DSI_VIDEO		= 13,
	LOMBO_VO_DEV_MIPI_DSI_CMD		= 14,
	LOMBO_VO_DEV_BT601_24BIT		= 15,
	LOMBO_VO_DEV_BT601_16BIT		= 16,
	LOMBO_VO_DEV_BT601_8BIT			= 17,
	LOMBO_VO_DEV_BT656_8BIT			= 18,
	LOMBO_VO_DEV_BT1120_16BIT		= 19,
	LOMBO_VO_DEV_BT1120_8BIT		= 20,
	LOMBO_VO_DEV_TVE			= 21,
	LOMBO_VO_DEV_EDP			= 22,
} lombo_vo_dev_if_t;

/* LOMBO VO mode flags */

/* hsync valid on pos edge */
#define LOMBO_VO_MODE_PHSYNC		BIT(0)
/* vsync valid on pos edge */
#define LOMBO_VO_MODE_PVSYNC		BIT(1)
/* DE valid level */
#define LOMBO_VO_MODE_PDATA		BIT(2)
/* clock valid level */
#define LOMBO_VO_MODE_PCLOCK		BIT(3)
/* vo data order */
#define LOMBO_VO_MODE_DATA_LSB_TO_MSB	BIT(4)
/* send hsync cmd */
#define LOMBO_VO_MODE_HSYNC_CMD		BIT(5)
/* send vsync cmd */
#define LOMBO_VO_MODE_VSYNC_CMD		BIT(6)
/* for interface with TE signal, like CPU-I8080 */
#define LOMBO_VO_MODE_WITH_TE		BIT(7)
/* TE signal POL */
#define LOMBO_VO_MODE_POS_TE		BIT(8)
/* lvds mode(JEIDA or VESA) */
#define LOMBO_VO_MODE_JEIDA		BIT(9)

struct lombo_vo_device {
	struct lombo_vo_host *host;
	struct device dev;
	char name[VO_DEV_NAME_SIZE];
	unsigned int channel;

	lombo_vo_dev_if_t interface;
	u32 cmd_align;
	enum lombo_vo_pixel_format format;
	unsigned long mode_flags;
	u32 clk_dly;
};

#define LOMBO_VO_MODULE_PREFIX "lombo-vo:"

static inline struct lombo_vo_device *to_lombo_vo_device(struct device *dev)
{
	return container_of(dev, struct lombo_vo_device, dev);
}

int lombo_vo_host_register(struct lombo_vo_host *host);
void lombo_vo_host_unregister(struct lombo_vo_host *host);
struct lombo_vo_host *of_find_lombo_vo_host_by_node(struct device_node *node);

struct lombo_vo_device *
lombo_vo_device_register_full(struct lombo_vo_host *host,
			      const struct lombo_vo_device_info *info);
void lombo_vo_device_unregister(struct lombo_vo_device *vo);
struct lombo_vo_device *of_find_lombo_vo_device_by_node(struct device_node *np);
int lombo_vo_attach(struct lombo_vo_device *vo);
int lombo_vo_detach(struct lombo_vo_device *vo);

ssize_t lombo_vo_write_buffer(struct lombo_vo_device *vo,
					void *data, size_t len);

/**
 * struct lombo_vo_driver - LOMBO VO driver
 * @driver: device driver model driver
 * @probe: callback for device binding
 * @remove: callback for device unbinding
 * @shutdown: called at shutdown time to quiesce the device
 */
struct lombo_vo_driver {
	struct device_driver driver;
	int (*probe)(struct lombo_vo_device *vo);
	int (*remove)(struct lombo_vo_device *vo);
	void (*shutdown)(struct lombo_vo_device *vo);
};

static inline struct lombo_vo_driver *
to_lombo_vo_driver(struct device_driver *driver)
{
	return container_of(driver, struct lombo_vo_driver, driver);
}

static inline void *lombo_vo_get_drvdata(const struct lombo_vo_device *vo)
{
	return dev_get_drvdata(&vo->dev);
}

static inline void lombo_vo_set_drvdata(struct lombo_vo_device *vo, void *data)
{
	dev_set_drvdata(&vo->dev, data);
}

int lombo_vo_driver_register_full(struct lombo_vo_driver *driver,
				  struct module *owner);
void lombo_vo_driver_unregister(struct lombo_vo_driver *driver);

#define lombo_vo_driver_register(driver) \
	lombo_vo_driver_register_full(driver, THIS_MODULE)

#define module_lombo_vo_driver(__lombo_vo_driver) \
	module_driver(__lombo_vo_driver, lombo_vo_driver_register, \
			lombo_vo_driver_unregister)

#endif /* #ifndef _LOMBO_VO_MANAGE_H_ */
